/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.persistence.basis.manager;

import java.util.Collection;

import com.genia.toolbox.persistence.criteria.Criteria;
import com.genia.toolbox.persistence.criteria.criterion.Criterion;
import com.genia.toolbox.persistence.criteria.criterion.Junction;
import com.genia.toolbox.persistence.criteria.enumeration.MatchMode;
import com.genia.toolbox.persistence.criteria.field.Field;
import com.genia.toolbox.persistence.exception.AggregateException;
import com.genia.toolbox.persistence.manager.RestrictionsManager;

/**
 * This class represents a basis for the generic RestrictionsManager that allows
 * user to generate restriction to use with criteria. It generates some
 * criterion from others.
 */
public abstract class AbstractRestrictionsManager
    extends AbstractManager
    implements RestrictionsManager
{

  /**
   * Return a criterion that represents the conjunction of two criterion. This
   * method use <code>conjunction</code> to generate the resulting
   * <code>Criterion</code>.
   * 
   * @param criterion1
   *          the first criterion
   * @param criterion2
   *          the seconde criterion
   * @return a criterion representation <code> criterion1 && criterion2</code>
   */
  public Criterion and(final Criterion criterion1, final Criterion criterion2)
  {
    return simpleJunction(conjunction(), criterion1, criterion2);
  }



  /**
   * Return a <code>Criterion</code> that always deny. This method uses the
   * negation of <code>accept</code>.
   * 
   * @return a <code>Criterion</code> that always deny
   * @see com.genia.toolbox.persistence.manager.RestrictionsManager#deny()
   */
  public Criterion deny()
  {
    return not(accept());
  }



  /**
   * Return a criterion that represents that a field match (without case)
   * another. This method use <code>FieldManager.lowerCase</code> as well as
   * the usual <code>like</code> to generate the resulting
   * <code>Criterion</code>.
   * 
   * @param field1
   *          the first field in the matching
   * @param field2
   *          the second field in the matching
   * @param matchMode
   *          the type of match
   * @return a criterion representing that <code>field1</code> match (without
   *         case) <code>field2</code>
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public Criterion ilike(final Field field1, final Field field2, final MatchMode matchMode)
      throws AggregateException
  {
    return like(getPersistenceManager().getFieldManager().lowerCase(field1), getPersistenceManager().getFieldManager().lowerCase(field2), matchMode);
  }



  /**
   * Return a criterion that represents that a field is not <code>null</code>.
   * This method uses the negation of <code>isNull</code> to generate the
   * criterion.
   * 
   * @param field
   *          the field to check
   * @return a criterion that represents that a field is not <code>null</code>
   */
  public Criterion isNotNull(final Field field)
  {
    return not(isNull(field));
  }



  /**
   * Return a criterion that represents that two fields are not the same. That
   * means that the two field are not equals and that the two fields are not
   * <code>null</code>. This method uses the negation of <code>isSame</code>
   * to generate the criterion.
   * 
   * @param field1
   *          the first field in the inequality
   * @param field2
   *          the second field in the inequality
   * @return a criterion representing <code>field1 != field2</code>
   */
  public Criterion isNotSame(final Field field1, final Field field2)
  {
    return not(isSame(field1, field2));
  }



  /**
   * Return a criterion that represents that two fields are the same. That means
   * that either the two field are equals, or that the two fields are
   * <code>null</code>. This method use <code>equals</code> and
   * <code>isNull</code> to generate the criterion.
   * 
   * @param field1
   *          the first field in the equality
   * @param field2
   *          the second field in the equality
   * @return a criterion representing <code>field1 == field2</code>
   */
  public Criterion isSame(final Field field1, final Field field2)
  {
    final Junction result = disjunction();

    result.add(equals(field1, field2));

    final Junction conjunction = conjunction();
    conjunction.add(isNull(field1));
    conjunction.add(isNull(field2));

    result.add(conjunction);

    return result;
  }



  /**
   * Return a criterion that represents that a field is lesser or equals to
   * another. This method returns <code>greaterOrEquals(field2, field1)</code>.
   * 
   * @param field1
   *          the first field in the comparaison
   * @param field2
   *          the second field in the comparaison
   * @return a criterion representing <code>field1 <= field2</code>
   */
  public Criterion lesserOrEquals(final Field field1, final Field field2)
  {
    return greaterOrEquals(field2, field1);
  }



  /**
   * Return a criterion that represents that a field is lesser than another.
   * This method returns <code>greaterThan(field2, field1)</code>
   * 
   * @param field1
   *          the first field in the comparaison
   * @param field2
   *          the second field in the comparaison
   * @return a criterion representing <code>field1 < field2</code>
   */
  public Criterion lesserThan(final Field field1, final Field field2)
  {
    return greaterThan(field2, field1);
  }



  /**
   * Return a criterion that represents the inequality between two fields. This
   * method returns <code>not(equals(field1, field2))</code>.
   * 
   * @param field1
   *          the first field in the inequality
   * @param field2
   *          the second field in the inequality
   * @return a criterion representing <code>field1 != field2</code>
   */
  public Criterion notEquals(final Field field1, final Field field2)
  {
    return not(equals(field1, field2));
  }



  /**
   * Return a criterion that represents that a field is not contained in a
   * collection of objects. This method returns
   * <code>not(isIn(field, collection))</code>
   * 
   * @param field
   *          the field to search
   * @param collection
   *          the collection the field must not be in
   * @return a criterion representing that <code>field</code> is not contained
   *         in <code>collection</code>
   */
  public Criterion notIn(final Field field, final Collection<?> collection)
  {
    return not(isIn(field, collection));
  }



  /**
   * Return a criterion that represents that a field is not contained is the
   * main result of another query. This method returns
   * <code>not(isIn(field, criteria))</code>
   * 
   * @param field
   *          the field to search
   * @param criteria
   *          the second query
   * @return a criterion representing that <code>field</code> is not contained
   *         in the main result of <code>criteria</code>
   */
  public Criterion notIn(final Field field, final Criteria<?> criteria)
  {
    return not(isIn(field, criteria));
  }



  /**
   * Return a criterion that represents that a field is not contained in another
   * field (the second field must be a collection). This method returns
   * <code>not(isIn(field1, field2))</code>
   * 
   * @param field1
   *          the field to search
   * @param field2
   *          the field to search in
   * @return a criterion representing that <code>field1</code> is not
   *         contained in the field <code>field2</code>
   */
  public Criterion notIn(final Field field1, final Field field2)
  {
    return not(isIn(field1, field2));
  }



  /**
   * Return a criterion that represents the disjunction of two criterion. This
   * method use <code>disjunction</code> to generate the resulting
   * <code>Criterion</code>.
   * 
   * @param criterion1
   *          the first criterion
   * @param criterion2
   *          the seconde criterion
   * @return a criterion representation <code> criterion1 && criterion2</code>
   */
  public Criterion or(final Criterion criterion1, final Criterion criterion2) // NOPMD
  {
    return simpleJunction(disjunction(), criterion1, criterion2);
  }



  /**
   * This method fill a junction with two criterions.
   * 
   * @param junction
   *          the junction to be filled
   * @param criterion1
   *          the first criterion to add to the junction
   * @param criterion2
   *          the second criterion to add to the junction
   * @return <code>junction</code>, once filled by <code>criterion1</code>
   *         and <code>criterion2</code>
   */
  private Criterion simpleJunction(final Junction junction, final Criterion criterion1, final Criterion criterion2)
  {
    junction.add(criterion1);
    junction.add(criterion2);
    return junction;
  }
}
